﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Security.Cryptography;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Web.Security;
using System.Collections.Specialized;
using System.Configuration.Provider;
using TheCollectiveNetwork.Classes.GlobalFunctions;
using TheCollectiveNetwork.Classes.Globals;
using TheCollectiveNetwork.Classes.Debugging;

namespace TheCollectiveNetwork.Classes.Security
{
               

        // Implements the TCN Role provider
        // GetRolesForUser is implemented so that [RequiresRole] attribute in service files works
        public class TCNRoleProvider : RoleProvider 
        {
            /* ---- Variables ---- */
            private string _ApplicationName;           
            private string _name;


            public override void Initialize(string pName, NameValueCollection pConfig)
            {                
                _ApplicationName = pConfig["applicationName"];
                _name = pName;

            }

            
            /* ---- Properties ---- */
            public override string Name
            {
                get { return _name; }                
            }

            public override string ApplicationName
            {
                get { return _ApplicationName; }
                set { _ApplicationName = value; }
            }

            
            /* ---- Public functions ---- */

            public override void AddUsersToRoles(string[] usernames, string[] rolenames)
            {
                
            }

                       
            public override void CreateRole(string rolename)
            {
                
            }

                      

            public override bool DeleteRole(string rolename, bool throwOnPopulatedRole)
            {                
                return false;
            }

                       

            public override string[] GetAllRoles()
            {                
                return new string[0];
            }


            public override string[] GetRolesForUser(string username)
            {
                string rsql;
                SqlCommand sqlcmd;
                SqlConnection dbconn;
                SqlDataReader sqlreader;
                List<string> roles;
                string[] rolesarray;

                dbconn = new SqlConnection(ConfigurationManager.ConnectionStrings[constring.pub_str].ConnectionString);

                rsql = "select RoleNumber from accounts_user left outer join accounts_user_roles on accounts_user.user_guid = accounts_user_roles.user_guid where accounts_user.username = @username;";

                sqlcmd = new SqlCommand(rsql, dbconn);
                sqlcmd.Parameters.AddWithValue("@username", username);

                dbconn.Open();
                sqlreader = sqlcmd.ExecuteReader(CommandBehavior.CloseConnection);


                roles = new List<string>();
                while (sqlreader.Read() == true)
                { roles.Add(sqlreader.GetInt32(0).ToString()); }

                sqlreader.Close();
                dbconn.Close();

                rolesarray = roles.ToArray();
                
                return rolesarray;
                
            }

                       
            public override string[] GetUsersInRole(string rolename)
            {                
                return new string[0];
            }


            public override bool IsUserInRole(string username, string rolename)
            {
                return false;
            }


            public override void RemoveUsersFromRoles(string[] usernames, string[] rolenames)
            {
               
            }


            public override bool RoleExists(string rolename)
            {                   
                return false;
            }


            public override string[] FindUsersInRole(string rolename, string usernameToMatch)
            {              
                return new string[0];
            }



        }


        // Implements the TCN Membership Provider
        public class TCNMembershipProvider : MembershipProvider
        {
            /* Variables */
            private string _ApplicationName;
            private string _name;
                        

            /* Initliase Method */

            public override void Initialize(string pName, NameValueCollection pConfig)
            {
                base.Initialize(pName, pConfig);

                _ApplicationName = pConfig["applicationName"];
                _name = pName;
            }




            /* -------- Properties ----------- */
            public override string Name
            {
                get { return _name; }
            }

            public override string ApplicationName
            {
                get { return _ApplicationName; }
                set { _ApplicationName = value; }
            }

           
            public override bool EnablePasswordReset
            {
                get { throw new NotImplementedException(); }
            }

            public override bool EnablePasswordRetrieval
            {
                get { throw new NotImplementedException(); }
            }

            public override bool RequiresQuestionAndAnswer
            {
                get { throw new NotImplementedException(); }
            }

            public override bool RequiresUniqueEmail
            {
                get { throw new NotImplementedException(); }
            }

            public override MembershipPasswordFormat PasswordFormat
            {
                get { throw new NotImplementedException(); }
            }

            public override int MaxInvalidPasswordAttempts
            {
                get { throw new NotImplementedException(); }
            }
                            

            public override int PasswordAttemptWindow
            {
                get { throw new NotImplementedException(); }
            }


            public override string PasswordStrengthRegularExpression
            {
                get { throw new NotImplementedException(); }
            }

            public override int MinRequiredNonAlphanumericCharacters
            {
                get { throw new NotImplementedException(); }
            }

            public override int MinRequiredPasswordLength
            {
                get { throw new NotImplementedException(); }
            }

           
            /* -------- Methods ----------- */
            public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
            {
                throw new NotImplementedException();
            }

            public override void UpdateUser(MembershipUser user)
            {
                throw new NotImplementedException();
            }

            public override bool DeleteUser(string username, bool deleteAllRelatedData)
            {
                throw new NotImplementedException();
            }


            public override bool ValidateUser(string p_username, string p_password)
            {
                /* Access Levels
                   * Disabled
                   * 101. Admin
                   * 102. Edit All             
                   * 103. Read All  
                   * 104. Normal User
                */

                string passwordencrypt;
                string filteredusername;
                SqlCommand sqlcmd;
                SqlConnection dbconn;
                SqlParameter ValidatedParam;
                
                bool IsValid;
              
                // Sleep - for security - to slow down multiple validation attempts
                Thread.Sleep(500);

                // Init variables
                IsValid = false;
                filteredusername = String.Empty;
                passwordencrypt = String.Empty;
                
                // Filter out any unwanted characters
                if (String.IsNullOrEmpty(p_username) == false)
                {
                    filteredusername = Regex.Replace(strfunc.Left(p_username, 255), "[^0-9a-z'\"~@_\\-\\.]", string.Empty, RegexOptions.IgnoreCase);
                }

                if (String.IsNullOrEmpty(p_password) == false)
                {
                    passwordencrypt = sha512encrypt(Regex.Replace(strfunc.Left(p_password, 255), "[^0-9a-z'\"~@_\\-\\.]", string.Empty, RegexOptions.IgnoreCase));
                }
                                                             
                // Check credentials against the database
                dbconn = new SqlConnection(ConfigurationManager.ConnectionStrings[constring.pub_str].ConnectionString);

                sqlcmd = new SqlCommand("Security_LoginUser", dbconn);
                sqlcmd.CommandType = CommandType.StoredProcedure;

                // Input Parameters
                sqlcmd.Parameters.AddWithValue("@username", filteredusername);
                sqlcmd.Parameters.AddWithValue("@passwordencrypt", passwordencrypt);

                // Output Parameters
                ValidatedParam = sqlcmd.Parameters.Add("@validated", SqlDbType.Bit);
                ValidatedParam.Direction = ParameterDirection.Output;               

                // Execute the query
                dbconn.Open();
                sqlcmd.ExecuteNonQuery();
                dbconn.Close();
                
                // Get the return parameters
                IsValid = Convert.ToBoolean(sqlcmd.Parameters["@validated"].Value);
                                
                return IsValid;

            }

            public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
            {
                throw new NotImplementedException();
               
            }

            public override MembershipUser GetUser(string username, bool userIsOnline)
            {
                throw new NotImplementedException();
                              
            }

            public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
            {
                throw new NotImplementedException();
            }

            public override int GetNumberOfUsersOnline()
            {
                throw new NotImplementedException();
            }

            public override string ResetPassword(string username, string answer)
            {
                throw new NotImplementedException();
            }

            public override string GetPassword(string username, string answer)
            {
                throw new NotImplementedException();
            }

            public override string GetUserNameByEmail(string email)
            {
                throw new NotImplementedException();
            }

            public override bool ChangePassword(string p_username, string p_oldPassword, string p_newPassword)
            {
                throw new NotImplementedException();
            }

            
            public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
            {
                throw new NotImplementedException();
            }

            public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
            {
                throw new NotImplementedException();
            }

            public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
            {
                throw new NotImplementedException();
            }

            public override bool UnlockUser(string userName)
            {
                throw new NotImplementedException();
            }

            // Custom functions not overriden
            public bool RequiresPasswordChange(string p_username)
            {                              
                string rsql;
                SqlCommand sqlcmd;
                SqlConnection dbconn;
                SqlDataReader sqlreader;
                DateTime PasswordLastChanged;
                bool MustChange;
                
                // Initialise
                PasswordLastChanged = DateTime.MinValue;

                // database connection
                dbconn = new SqlConnection(ConfigurationManager.ConnectionStrings[constring.pub_str].ConnectionString);

                // SQL statement
                rsql = "SELECT password_lastchanged FROM Accounts_User WHERE account_disabled = 0 and username=@username;";

                // Create command and add parameters
                sqlcmd = new SqlCommand(rsql, dbconn);
                sqlcmd.Parameters.AddWithValue("@username", p_username);

                // Open and execute the statement
                dbconn.Open();
                sqlreader = sqlcmd.ExecuteReader(CommandBehavior.CloseConnection);

                // Get the record value and put it in to a variable
                if (sqlreader.Read() == true)
                {
                    if (!sqlreader.IsDBNull(0))
                    {
                        PasswordLastChanged = sqlreader.GetDateTime(0);
                    }
                }

                // Close the database connection
                sqlreader.Close();
                dbconn.Close();

                // Determin if the password needs to be changed
                if (PasswordLastChanged.AddDays(appstring.PasswordMaxAgeDays) <= DateTime.Now)
                {
                    MustChange = true;
                }
                else
                {
                    MustChange = false;
                }
                

                return MustChange;
            }

            public TheCollectiveNetwork.Web.Services.DTOreturnmessage TCNChangePassword(string p_username, string p_oldPassword, string p_newPassword)
            {
                SqlCommand sqlcmd;
                SqlConnection dbconn;
                SqlParameter ReturnCodeParam;
                SqlParameter ReturnMessageParam;
                TheCollectiveNetwork.Web.Services.DTOreturnmessage DTOMsg;
                TheCollectiveNetwork.Web.Services.DTOreturnmessage DTOMsgPasswordCompliant;
                                
                DTOMsgPasswordCompliant = IsPasswordCompliant(p_newPassword);
                                
                if (DTOMsgPasswordCompliant.returncode == 0)
                {
                    // Check credentials against the database
                    dbconn = new SqlConnection(ConfigurationManager.ConnectionStrings[constring.write_str].ConnectionString);

                    sqlcmd = new SqlCommand("Security_ChangePassword", dbconn);
                    sqlcmd.CommandType = CommandType.StoredProcedure;

                    // Input Parameters
                    sqlcmd.Parameters.AddWithValue("@username", p_username);
                    sqlcmd.Parameters.AddWithValue("@oldpasswordencrypt", sha512encrypt(p_oldPassword));
                    sqlcmd.Parameters.AddWithValue("@newpasswordencrypt", sha512encrypt(p_newPassword));

                    // Output Parameters
                    ReturnCodeParam = sqlcmd.Parameters.Add("@returncode", SqlDbType.Int);
                    ReturnCodeParam.Direction = ParameterDirection.Output;

                    ReturnMessageParam = sqlcmd.Parameters.Add("@returnmessage", SqlDbType.VarChar, 200);
                    ReturnMessageParam.Direction = ParameterDirection.Output;

                    // Execute the query
                    dbconn.Open();
                    sqlcmd.ExecuteNonQuery();
                    dbconn.Close();

                    DTOMsg = new TheCollectiveNetwork.Web.Services.DTOreturnmessage();
                    DTOMsg.returncode = Convert.ToInt32(ReturnCodeParam.Value);
                    DTOMsg.returnmessage = Convert.ToString(ReturnMessageParam.Value);
                }
                else
                {
                    DTOMsg = DTOMsgPasswordCompliant;
                }

                return DTOMsg;
            }

            public TheCollectiveNetwork.Web.Services.DTOreturnmessage IsPasswordCompliant(string p_password)
            {                
                  
                TheCollectiveNetwork.Web.Services.DTOreturnmessage DTOMsg;
                DTOMsg = new TheCollectiveNetwork.Web.Services.DTOreturnmessage();
                            
                              
                if (p_password.Length <= appstring.PasswordMinLength)
                {
                    DTOMsg.returncode = 1;
                    DTOMsg.returnmessage = @"Password is not at least " + appstring.PasswordMinLength.ToString() + @" characters.";
                }
                else
                {
                    DTOMsg.returncode = 0;
                    DTOMsg.returnmessage = String.Empty;
                }
                
                
                return DTOMsg;
            }
           
            //Hash string encryption for the password
            public static string sha512encrypt(string phrase)
            {
                StringBuilder output = new StringBuilder("");

                UTF8Encoding encoder = new UTF8Encoding();
                SHA512Managed sha512hasher = new SHA512Managed();
                byte[] hashedDataBytes = sha512hasher.ComputeHash(encoder.GetBytes(phrase));

                for (int i = 0; i < hashedDataBytes.Length; i++)
                {
                    output.Append(hashedDataBytes[i].ToString("X2"));
                }
                return output.ToString();
            }


            /* Private Functions */

        }
           

}
